/**
 * Software License, Version 1.0
 * 
 * Copyright 2008 The Trustees of Indiana University.  All rights reserved.
 * 
 *
 *Redistribution and use in source and binary forms, with or without 
 *modification, are permitted provided that the following conditions are met:
 *
 *1) All redistributions of source code must retain the above copyright notice,
 * the list of authors in the original source code, this list of conditions and
 * the disclaimer listed in this license;
 *2) All redistributions in binary form must reproduce the above copyright 
 * notice, this list of conditions and the disclaimer listed in this license in
 * the documentation and/or other materials provided with the distribution;
 *3) Any documentation included with all redistributions must include the 
 * following acknowledgment:
 *
 *"This product includes software developed by the Community Grids Lab. For 
 * further information contact the Community Grids Lab at 
 * http://communitygrids.iu.edu/."
 *
 * Alternatively, this acknowledgment may appear in the software itself, and 
 * wherever such third-party acknowledgments normally appear.
 * 
 *4) The name Indiana University or Community Grids Lab or Granules, 
 * shall not be used to endorse or promote products derived from this software 
 * without prior written permission from Indiana University.  For written 
 * permission, please contact the Advanced Research and Technology Institute 
 * ("ARTI") at 351 West 10th Street, Indianapolis, Indiana 46202.
 *5) Products derived from this software may not be called Granules, 
 * nor may Indiana University or Community Grids Lab or Granules appear
 * in their name, without prior written permission of ARTI.
 * 
 *
 * Indiana University provides no reassurances that the source code provided 
 * does not infringe the patent or any other intellectual property rights of 
 * any other entity.  Indiana University disclaims any liability to any 
 * recipient for claims brought by any other entity based on infringement of 
 * intellectual property rights or otherwise.  
 *
 * LICENSEE UNDERSTANDS THAT SOFTWARE IS PROVIDED "AS IS" FOR WHICH NO 
 * WARRANTIES AS TO CAPABILITIES OR ACCURACY ARE MADE. INDIANA UNIVERSITY GIVES
 * NO WARRANTIES AND MAKES NO REPRESENTATION THAT SOFTWARE IS FREE OF 
 * INFRINGEMENT OF THIRD PARTY PATENT, COPYRIGHT, OR OTHER PROPRIETARY RIGHTS. 
 * INDIANA UNIVERSITY MAKES NO WARRANTIES THAT SOFTWARE IS FREE FROM "BUGS", 
 * "VIRUSES", "TROJAN HORSES", "TRAP DOORS", "WORMS", OR OTHER HARMFUL CODE.  
 * LICENSEE ASSUMES THE ENTIRE RISK AS TO THE PERFORMANCE OF SOFTWARE AND/OR 
 * ASSOCIATED MATERIALS, AND TO THE PERFORMANCE AND VALIDITY OF INFORMATION 
 * GENERATED USING SOFTWARE.
 */
package cgl.granules.samples.bioinf;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Properties;
import java.util.StringTokenizer;

import cgl.granules.dataset.DatasetCollection;
import cgl.granules.dataset.DatasetException;
import cgl.granules.dataset.DatasetFactory;
import cgl.granules.dataset.FileAccess;
import cgl.granules.exception.CommunicationsException;
import cgl.granules.exception.DeploymentException;
import cgl.granules.exception.MarshallingException;
import cgl.granules.operation.InstanceDeployer;
import cgl.granules.operation.JobSubmission;
import cgl.granules.operation.MapReduceException;
import cgl.granules.operation.ProgressTracker;

/**
 * A bio-informatics example demonstrating the use of the CAP3 sequence assembly
 * program to assemble mRNA fragments on a distributed set of resources.
 * 
 * @author Shrideep Pallickara
 * 
 */
public class ClusterDeployer extends InstanceDeployer {
    private ProgressTracker progressTracker;


    /**
     * Initializes the deployer.
     * 
     * @param streamingProperties
     *            The properties for streaming
     * @param commType
     *            The communications type
     * @throws CommunicationsException
     *             If there are network problems during communications
     * @throws IOException
     *             If there are IO problems
     * @throws MarshallingException
     *             If there are marshalling erros
     * @throws DeploymentException
     *             If there are deployment related problems
     */
    public ClusterDeployer(Properties streamingProperties, String commType)
        throws CommunicationsException, IOException, MarshallingException,
        DeploymentException {
        initialize(streamingProperties, commType);
    }


    /**
     * Initializes a cluster map for deployment.
     * 
     * @param mapNum
     *            The ID associated with this particular instance.
     * @param directory
     *            The working directory of the subprocess where the command
     *            would be executed.
     * @param filenameBase
     *            The File which contains the list of files that need to be
     *            processed.
     * @param numOfMaps
     *            The total number of maps that will be deployed.
     * @param numOfFiles
     *            The total number of files that have to be processed.
     * @return The initialized ClusterMap which will process a set of files.
     * @throws DatasetException
     *             If there are problems initializing the datasets
     * @throws MapReduceException
     *             If there is a MapReduceExcecution because of problems when
     *             setting up the roles.
     * @throws IOException
     *             If there are IO problems.
     */
    private ClusterMap initializeClusterMap(int mapNum, String directory,
                                            String filenameBase, int numOfMaps,
                                            int numOfFiles)
        throws DatasetException, MapReduceException, IOException {
        ClusterMap map = new ClusterMap();

        Properties props = new Properties();
        String value = "(" + mapNum + ")";
        String property = "Worker Number";
        props.put(property, value);
        props.put("Directory", directory);

        map.setProcessingDirectives(props);

        DatasetFactory datasetFactory = DatasetFactory.getInstance();
        FileAccess fileAccess =
            datasetFactory.createFileDataset("FileDataset-ID-BioInf",
                                             "Bio Cluster example");

        BufferedReader in =
            new BufferedReader(new FileReader(filenameBase), 65536);
        String filename = "";
        for (int i = 0; i < numOfFiles; i++) {
            filename = in.readLine();

            if (i % numOfMaps == mapNum) {
                // String filename = filenameBase + i + ".fsa";
                filename = "/globalhome/username/dataset/2mil/" + filename;

                fileAccess.addFile(filename, "r");
            }/* the modulo-check */
        }

        in.close();

        DatasetCollection datasetCollection = map.getDatasetCollection();
        datasetCollection.addDataset(fileAccess);
        System.out.println("Initialized Map (" + mapNum + ")");
        return map;
    }


    /**
     * Prepares a set of instances to be deployed. The various arguments that
     * this method takes are described below.
     * 
     * @param directory
     *            The working directory of the subprocess where the command
     *            would be executed.
     * @param filenameBase
     *            The File which contains the list of files that need to be
     *            processed.
     * @param numOfMaps
     *            The total number of maps that will be deployed.
     * @param numOfFiles
     *            The total number of files that have to be processed.
     * @throws DatasetException
     *             If there are problems initializing the datasets
     * @throws MapReduceException
     *             If there is a MapReduceExcecution because of problems when
     *             setting up the roles.
     * @throws IOException
     *             If there are IO problems.
     * @throws CommunicationsException
     *             If there are network problems during communications
     * @throws DeploymentException
     *             If there are deployment related problems
     * @throws MarshallingException
     *             If there are marshalling problems.
     */
    public void prepareBaseInstancesAndDeploy(String directory,
                                              String filenameBase,
                                              int numOfMaps, int numOfFiles)
        throws DatasetException, MapReduceException, IOException,
        CommunicationsException, DeploymentException, MarshallingException {
        ClusterMap maps[] = new ClusterMap[numOfMaps];

        for (int i = 0; i < numOfMaps; i++) {
            maps[i] =
                initializeClusterMap(i, directory, filenameBase, numOfMaps,
                                     numOfFiles);
        }

        setStartTime();
        progressTracker = deployOperations(maps);

    }


    public static void main(String[] args) {
        try {
            String module = "ClusterDeployer.main()->";
            if (args.length < 2) {
                System.out.println("Usage: java cgl.granules.samples.bioinf."
                    + "ClusterDeployer <hostname> <port>");
                System.exit(0);
            }
            String hostname = args[0];
            String portnum = args[1];
            String commType = "niotcp";
            if (args.length == 3) {
                commType = args[2];
            }

            Properties streamingProperties = new Properties();
            streamingProperties.put("hostname", hostname);
            streamingProperties.put("portnum", portnum);

            ClusterDeployer deployer =
                new ClusterDeployer(streamingProperties, commType);

            JobSubmission jobber = JobSubmission.getInstance();
            jobber.initialize(null, 87909, streamingProperties, commType);

            String helpInfo =
                "\n\nThis is a test program that is used to assemble mRNA "
                    + " sequences using the CAP3 program. You need to have an "
                    + "installation of CPA3 on the resource that you plan to "
                    + "execute the routines on. The current list of commands include: \n\n"
                    + "submit <directory_to_execute_in> <file_with_list_of_files> "
                    + "<num_of_maps> <num_of_files>"
                    + "\n\t    {Launches multiple maps on multiple resources} \n"
                    + "status    : Lists the status of all tasks within the Job\n"
                    + "h         : List the set of commands \n\n";

            InputStreamReader isr = new InputStreamReader(System.in);
            BufferedReader commandLine = new BufferedReader(isr);

            System.out.println(helpInfo);

            StringTokenizer tok;
            while (true) {
                String s = commandLine.readLine();
                if (s == null) {
                    System.out.println(module + "String is null!!!");
                    break;
                }

                if (s.startsWith("submit")) {
                    deployer.resetTrackers(); /* utility counts */
                    System.out.println(module
                        + "Processing the [cluster-submit-bm] command");
                    String directory = ".";
                    String filename_base =
                        "/globalhome/username/granules/test/wc_data_";
                    int numOfMaps = 256;
                    int numOfFiles = 256;

                    tok = new StringTokenizer(s, " ");
                    int numOfTokens = tok.countTokens();
                    tok.nextToken();/* skip the command */

                    if (numOfTokens > 1) {
                        directory = tok.nextToken();
                    }

                    if (numOfTokens > 2) {
                        filename_base = tok.nextToken();
                    }

                    if (numOfTokens > 3) {
                        numOfMaps = Integer.parseInt(tok.nextToken());
                    }

                    if (numOfTokens > 4) {
                        numOfFiles = Integer.parseInt(tok.nextToken());
                    }

                    String params =
                        "\nDirectory for executable      : " + directory
                            + "\nFilename base      : " + filename_base
                            + "\nNumber of Maps     : " + numOfMaps
                            + "\nNumber of Files    :" + numOfFiles;
                    System.out.println(params);
                    deployer.prepareBaseInstancesAndDeploy(directory,
                                                           filename_base,
                                                           numOfMaps,
                                                           numOfFiles);
                    continue;
                }

                if (s.equals("h")) {
                    System.out.println(helpInfo);
                    continue;
                }

                if (s.equals("q")) {
                    System.out.print(module
                        + "Exiting the Instance Deployer");
                    break;
                }

                if (s.equals("status")) {
                    System.out.println(deployer.getStatus());
                }

            }/* end while (true) */

        } catch (Exception e) {
            System.out.println(e.getMessage());
            e.printStackTrace();
        }
    }
}
